﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Microsoft.WowAddonStudio.Lua.LanguageService.Model;
using Microsoft.WowAddonStudio.Lua.Loaders;
using Microsoft.WowAddonStudio.Lua.Model;

namespace Microsoft.WowAddonStudio.Lua.LanguageService.Intellisense
{
    /// <summary>
    /// An abstract base class for implementing the <see cref="IDeclarationProvider"/> interface that leverages a <see cref="DocumentationLoader"/>
    /// for loading the documentation.
    /// </summary>
    /// <typeparam name="TDocumentationLoader">The type of the documentation loader.</typeparam>
    public abstract class DocumentationDeclarationProvider<TDocumentationLoader> : QualifiedNameDeclarationProvider, IDocumentationDeclarationProvider where TDocumentationLoader : DocumentationLoader
    {
        private readonly TDocumentationLoader documentationLoader;
        private DocumentationScope documentationScope;

        /// <summary>
        /// Initializes a new instance of the <see cref="DocumentationDeclarationProvider{TDocumentationLoader}"/> class.
        /// </summary>
        /// <param name="documentationLoader">The documentation loader.</param>
        protected DocumentationDeclarationProvider(TDocumentationLoader documentationLoader)
        {
            if (documentationLoader == null)
                throw new ArgumentNullException("documentationLoader");

            this.documentationLoader = documentationLoader;
        }

        /// <summary>
        /// Gets the documentation scope.
        /// </summary>
        public DocumentationScope DocumentationScope
        {
            get
            {
                if (documentationScope == null)
                {
                    documentationScope = documentationLoader.LoadDocumentation();
                    OnDocumentationLoaded();
                }

                return documentationScope;
            }
        }

        /// <summary>
        /// Gets the documentation from the provider.
        /// </summary>
        /// <value></value>
        public Documentation Documentation
        {
            get { return this.DocumentationScope.Documentation; }
        }

        /// <summary>
        /// Gets the active scope that should be used for resolve operations.
        /// </summary>
        protected virtual DocumentationScope ActiveScope
        {
            get { return this.DocumentationScope; }
        }

        /// <summary>
        /// Called after the documentation is loaded using the <see cref="DocumentationLoader"/>.
        /// </summary>
        protected virtual void OnDocumentationLoaded()
        {
        }

        /// <summary>
        /// Gets the declarations.
        /// </summary>
        protected override IEnumerable<Declaration> GetDeclarations(QualifiedName qualifiedName)
        {
            return ActiveScope.Declarations.Where(delegate(Declaration declaration)
            {
                if (declaration is Table)
                    return !((Table)declaration).Virtual;

                return true;
            });
        }

        /// <summary>
        /// Gets the member select declarations.
        /// </summary>
        protected override IEnumerable<Declaration> GetMemberSelectDeclarations(QualifiedName qualifiedName)
        {
            var qualifiedTable = ActiveScope.Resolve<Table>(qualifiedName);
            if (qualifiedTable != null)
            {
                if (Request.IsMethodSelect)
                    return qualifiedTable.Declarations.Where(declaration => declaration is Function);

                return qualifiedTable.Declarations;
            }

            return base.GetMemberSelectDeclarations(qualifiedName);
        }


        /// <summary>
        /// Gets the methods.
        /// </summary>
        /// <returns>
        /// An enumerable collection of <see cref="Function"/> instances.
        /// </returns>
        protected override IEnumerable<Function> GetMethods(QualifiedName qualifiedName)
        {
            var qualifiedFunction = ActiveScope.Resolve<Function>(qualifiedName);
            if (qualifiedFunction != null)
                return new[]
                       {
                           qualifiedFunction
                       };

            return base.GetMethods(qualifiedName);
        }

        /// <summary>
        /// Gets the source of the declaration with the qualified name.
        /// </summary>
        /// <param name="qualifiedName"></param>
        /// <returns>
        /// An instance of the <see cref="DeclarationSource"/> class or null if no declaration source can be provided.
        /// </returns>
        protected override DeclarationSource GetDeclarationSource(QualifiedName qualifiedName)
        {
            var declaration = ActiveScope.Resolve<Declaration>(qualifiedName);
            if (declaration != null && declaration.SourceUri != null && declaration.Location != null)
            {
                return new DeclarationSource
                {
                    Uri = declaration.SourceUri,
                    Location = declaration.Location
                };
            }

            return null;
        }

        /// <summary>
        /// Gets a data tip for the current request.
        /// </summary>
        /// <returns>
        /// An instance of the <see cref="DataTip"/> class or null if no data tip can be provided.
        /// </returns>
        protected override DataTip GetDataTip(QualifiedName qualifiedName)
        {
            var qualifiedDeclaration = ActiveScope.Resolve<Declaration>(qualifiedName);
            if (qualifiedDeclaration != null)
                return new DataTip(qualifiedDeclaration.GetDescription(), qualifiedName.Location);

            return base.GetDataTip(qualifiedName);
        }
    }
}
